#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <pthread.h>
#include <sys/stat.h>

#include "rsc.h"
#include "debug.h"
#include "record.h"
#include "sdmng.h"
#include "exception.h"

#define MAX_VBUFF_SIZE (400*1024)
#define MAX_ABUFF_SIZE 1024
#define MAX_UDATA_SIZE 1024

struct record_sink 
{
    int 				stype; /** sink type,see @enum record_sink_type */
    int 				chn;
    char 				mpoint[64];//device mount point	
    char 				fname[128];//record file absolute path and name	
    time_t 				create_time;
    unsigned int 		file_size; // record file size ,unit bytes

    RSC_SINK_HANDLE		psink;
    int 				user_len;
    unsigned char 		*user_data;
    unsigned char 		*video_buf;
    unsigned char 		*audio_buf;
};

/******************* Data Variables used in RSC-record module *************/
static unsigned char rsc_video_buff[RSC_REC_MAX_CHN][MAX_VBUFF_SIZE];
/// audio buffer for video & audio interlace
static unsigned char rsc_audio_buff[RSC_REC_MAX_CHN][MAX_ABUFF_SIZE];
/// audio buffer for audio solo record
static unsigned char rsc_audio_solo[RSC_REC_MAX_CHN][MAX_ABUFF_SIZE];
/// user data buffer for audio solo record
static unsigned char rsc_user_data[RSC_REC_MAX_CHN][MAX_UDATA_SIZE];

struct rsc_record_param *rsc_default_cfg = NULL;

#if  0 
static void print_helper(const char *str,unsigned char *buf,int len)
{
    printf("\n%s:%d\n",str, len);
    int i = 0;
    for(i=1; i<=len; i++)
    {
	printf("0x%02x ",buf[i-1]);
	if(i%16 == 0)
	    printf("\n");
    }
    printf("\n");
}
#endif

static int run_rec = 1; 
static pthread_mutex_t rec_lock=PTHREAD_MUTEX_INITIALIZER;	 /// lock record params
static pthread_mutex_t cfg_file_lock=PTHREAD_MUTEX_INITIALIZER; /// lock record file rw
static int set_rec_param[RSC_REC_MAX_CHN];/// check global record param whether has changed

static struct rsc_record_param crnt_param;
static struct rsc_channel_param chn_param[RSC_REC_MAX_CHN];
static struct record_sink  rec_sinks[REC_DTYPE_MAX][RSC_REC_MAX_CHN];

static pthread_t record_tid[RSC_REC_MAX_CHN*2];

/******* record running ok status ****/
static int rec_status_val = 0;
static pthread_mutex_t rec_stat_mutex = PTHREAD_MUTEX_INITIALIZER;

static int count_recsink_size(st_rsc_rec_media *media, int time,int type)
{
    int size = 0;

    if(type == REC_DTYPE_VIDEO)
	size = ((unsigned long long)media->video_bitrate * time/8)>>20; // MB
    else 
	size = ((unsigned long long)media->audio_bitrate * time/8)>>20; // MB

    if(size == 0)
	size = 1;

    return size;
}

static pthread_mutex_t gen_name_lock = PTHREAD_MUTEX_INITIALIZER;
static char cur_record_dir[64]={0};
int rsc_get_cur_recdir(char dir[],int len)
{
    int dirlen = strlen(cur_record_dir);
    if(dir==NULL || len <= dirlen)
    {
	SLOG(ERR,"dir is null,or len[%d] <= dirlen[%d]\n",len,dirlen);
	return -1;
    }
    memset(dir,0,len);// clear last value 
    if(dirlen <= 0)
	return -1;
    pthread_mutex_lock(&gen_name_lock);
    strcpy(dir,cur_record_dir);
    pthread_mutex_unlock(&gen_name_lock);
    return 0;
}
static int create_sink_name(struct record_sink *recsink, int mode)
{
    char *dirpath = recsink->mpoint;
    int chn = recsink->chn;
    int suffix_start = rsc_default_cfg->control.chn_suffix_start;
    char dirname[64] = {0}, suffix[32]={0};
    struct tm *nowtime;
    time_t this_time = time(NULL);

    pthread_mutex_lock(&gen_name_lock);
    recsink->create_time = this_time;

    static time_t sync_rec_time = 0;

    if(mode==REC_MODE_AUTO)
    {
	if(this_time-sync_rec_time>=0 && this_time-sync_rec_time<=30)
	    recsink->create_time = sync_rec_time;
	else
	    sync_rec_time = this_time;
    }
    struct tm mytm;
    nowtime = localtime_r(&recsink->create_time,&mytm);
    snprintf(dirname,sizeof(dirname), "%s/%04d%02d%02d", dirpath, nowtime->tm_year+1900, 
	    nowtime->tm_mon+1, nowtime->tm_mday);

    /// need to mutex-mkdir the same dir
    mkdir(dirname, S_IRWXU | S_IRWXG | S_IRWXO);
    if (access(dirname, 0) != 0) 
    {
	SLOG(ERR, "%s doesnot exist.\n", dirname);
	pthread_mutex_unlock(&gen_name_lock);
	return -1;
    }

    if(suffix_start>=0)
	snprintf(suffix,sizeof(suffix),"_%d",chn+suffix_start);
    if(recsink->stype == REC_SINK_AUDIO)
    {
	switch(rsc_default_cfg->control.audio_pack)
	{
	    case REC_PACK_WAV:
	    case REC_PACK_AVI:strcat(suffix,".wav");break;
	    case REC_PACK_MP3:strcat(suffix,".mp3");break;
	    case REC_PACK_MAV:strcat(suffix,".mav");break;
	    default:strcat(suffix,".raw");break;
	}
    }
    else
    {
	switch(rsc_default_cfg->control.file_pack)
	{
	    case REC_PACK_AVI:strcat(suffix,".avi");break;
	    case REC_PACK_MAV:strcat(suffix,".mav");break;
	    default:strcat(suffix,".raw");break;
	}
    }
    sprintf(recsink->fname, "%s/%02d%02d%02d%s", dirname, 
	    nowtime->tm_hour, nowtime->tm_min, nowtime->tm_sec, suffix);

    /// use in sd loop delete to avoid delete current record file.
    strcpy(cur_record_dir,dirname);
    /// avoid same record file exists
    remove(recsink->fname);
    pthread_mutex_unlock(&gen_name_lock);

    return 0;
}

static int init_record_sink(struct record_sink *recsink,
	int chn, st_rsc_rec_media *media, st_rsc_rec_ctrl *ctrl,int type)
{
    pthread_mutex_lock(&rec_lock);

    //SLOG(DBG,"chanel number %d\n", param->chn);
    if(type == REC_DTYPE_AUDIO)
	recsink->stype = REC_SINK_AUDIO;
    else if(ctrl->audio_on)
	recsink->stype = REC_SINK_VAMIX;
    else 
	recsink->stype = REC_SINK_VIDEO;

    /** init all members */
    recsink->chn = chn;
    memset(recsink->mpoint,0,sizeof(recsink->mpoint));
    memset(recsink->fname,0,sizeof(recsink->fname));
    recsink->create_time = time(NULL);
    recsink->file_size = 0;
    recsink->psink = NULL;

    /** buffer init */
    recsink->user_len = 0;
    recsink->user_data = &rsc_user_data[chn][0];
    recsink->video_buf = &rsc_video_buff[chn][0];
    if(type == REC_DTYPE_AUDIO)
	recsink->audio_buf = &rsc_audio_solo[chn][0];
    else
	recsink->audio_buf = &rsc_audio_buff[chn][0];

    int size , total_chn, mode, tlen;
    total_chn = rsc_default_cfg->source.chn;
    mode = ctrl->mode;
    tlen = ctrl->file_time;
    if(type == REC_DTYPE_AUDIO)
	tlen = ctrl->audio_time;
    pthread_mutex_unlock(&rec_lock);

    size = count_recsink_size(media,tlen,type);

    SLOG(PRI,"estimate file size:[%d]MB,total:x%d = %d MB,timelen = %d Secs\n",
	    size,total_chn,size*total_chn,tlen);

    /** we suppose all channels is recording to ensure space is ok */
    if(rsc_get_avail_dev(size*total_chn, recsink->mpoint)<0)
    {
	SLOG(ERR,"rsc_get_avail_dev error!\n");
	pthread_mutex_lock(&rec_stat_mutex);
	rec_status_val = 0; 
	pthread_mutex_unlock(&rec_stat_mutex);
	return -1;
    }
    /// sd card is ok
    if(rec_status_val==0)
    {
	pthread_mutex_lock(&rec_stat_mutex);
	rec_status_val = 1; 
	pthread_mutex_unlock(&rec_stat_mutex);
    }

    if(create_sink_name(recsink,mode)<0)
    {
	SLOG(ERR, "create_sink_name error,mode = %d!\n",mode);
	return -1;
    }

    /** init sink */
    struct rsc_sink_cfg sink_cfg;
    memset(&sink_cfg,0,sizeof(sink_cfg));
    memcpy(&sink_cfg.mparam,media,sizeof(sink_cfg.mparam));
    memcpy(&sink_cfg.user_data,(void*)(recsink->user_data),recsink->user_len);
    sink_cfg.user_len = recsink->user_len;
    sink_cfg.chn = recsink->chn;

    recsink->psink = rsc_default_cfg->sink.init_sink(recsink->fname,
	    recsink->stype,&sink_cfg);
    if(recsink->psink == NULL)
    {
	SLOG(ERR,"init_sink fail!\n");
	return -1;
    }
    SLOG(PRI,"recsink name: %s\n",recsink->fname);
    return 0;
}

static int fill_record_sink(struct record_sink *recsink, int type)
{	
    if(recsink == NULL || type >= REC_DTYPE_MAX)
    {
	SLOG(ERR, "invalid parameter,type = %d!\n",type);
	return -1;
    }

    if(recsink->stype==REC_SINK_AUDIO && type==REC_DTYPE_VIDEO)
	return 0;
    else if(recsink->stype==REC_SINK_VIDEO && type==REC_DTYPE_AUDIO)
	return 0;

    int chn = recsink->chn;
    st_rsc_rec_source *source = &rsc_default_cfg->source;
    struct record_frame_info frame;
    int ret = 0;
    memset(&frame,0,sizeof(frame));
    if(recsink->stype == REC_SINK_AUDIO)
	chn += source->chn;
    switch(type)
    {
	case REC_DTYPE_VIDEO:
	    frame.addr = recsink->video_buf;
	    frame.size = MAX_VBUFF_SIZE;
	    source->preprocess[REC_DTYPE_VIDEO](&frame, source->video_ds[chn]);
	    break;
	case REC_DTYPE_AUDIO:
	    frame.addr = recsink->audio_buf;
	    frame.size = MAX_ABUFF_SIZE;
	    source->preprocess[REC_DTYPE_AUDIO](&frame, source->audio_ds[chn]);
	    break;
	default:
	    SLOG(WRN,"not supported type:%d\n",type);
	    return 0;
    }
    if (frame.flag==0 || frame.len <= 0)
    {
	//SLOG(WRN, "%s frame is broken,drop it,flag = %d,len = %d,chn = %d.\n",
	//		(type==REC_DTYPE_VIDEO)?"video":"audio",frame.flag,frame.len,chn);
	return 1;/// at most case indicates source is empty
    }
#if 0 
    print_helper("video data len",frame.addr,frame.len);
#endif			
    if((ret=rsc_default_cfg->sink.fill_sink(recsink->psink,type,&frame))<0)
    {
	SLOG(ERR, "write %s frame err,ret = %d!\n",(type==REC_DTYPE_VIDEO)?"video":"audio",ret);
	if(ret == RSC_EWRIT)
	    rsc_exception_handle(recsink->chn,DEV_STA_WRITE_ERR,
		    recsink->mpoint,recsink->fname);
	return ret;
    }

    /// add record file size by bytes
    recsink->file_size += frame.len;
    return 0;
}

static int deinit_record_sink(struct record_sink *recsink)
{
    if(recsink == NULL)
    {
	SLOG(ERR, "invalid parameter !\n");
	return -1;
    }

    return rsc_default_cfg->sink.exit_sink(recsink->psink);
}

static int save_record_param(struct rsc_record_param *param)
{
    int ret = 0;
    FILE *pf = NULL;

    if(param == NULL)
    {
	SLOG(ERR, "invalid parameter\n");
	return -1;
    }
    pthread_mutex_lock(&cfg_file_lock);
    pf = fopen(RECORD_CONFIG_FILE, "w+");
    if (pf == NULL) {
	SLOG(ERR, "fopen %s err\n", RECORD_CONFIG_FILE);
	pthread_mutex_unlock(&cfg_file_lock);
	return -1;
    }
    ret = fwrite(param, sizeof(struct rsc_record_param), 1, pf);
    if (ret != 1) {
	SLOG(ERR, "fwrite to %s err\n", RECORD_CONFIG_FILE);
	fclose(pf);
	pthread_mutex_unlock(&cfg_file_lock);
	return -1;
    }
    fclose(pf);
    pthread_mutex_unlock(&cfg_file_lock);

    return 0;
}

static int read_record_param(struct rsc_record_param *param)
{
    int ret = 0;
    FILE *pf = NULL;

    if(param == NULL)
    {
	SLOG(ERR, "invalid parameter\n");
	return -1;
    }

    pthread_mutex_lock(&cfg_file_lock);
    pf = fopen(RECORD_CONFIG_FILE, "r+");
    if (pf == NULL) {
	SLOG(ERR, "fopen %s err\n", RECORD_CONFIG_FILE);
	pthread_mutex_unlock(&cfg_file_lock);
	return -1;
    }

    ret = fread(param, sizeof(struct rsc_record_param), 1, pf);
    if (ret != 1) {
	SLOG(ERR, "fread to %s err\n", RECORD_CONFIG_FILE);
	fclose(pf);
	pthread_mutex_unlock(&cfg_file_lock);
	return -1;
    }	

    fclose(pf);
    pthread_mutex_unlock(&cfg_file_lock);
    return 0;
}

static int proc_record(int chn, st_rsc_rec_media *media,
	st_rsc_rec_ctrl *ctrl, int *valid, int type)
{
    int ret = 0;
    int pre_valid = *valid;// backup previous channel valid value

    if(NULL == media || NULL == ctrl) 
    {
	SLOG(ERR, "param invalid!\n"); 
	return -1;
    } 

    struct record_sink *recsink = &rec_sinks[type][chn];

    if(init_record_sink(recsink,chn,media,ctrl,type)<0)
    {
	SLOG(ERR,"init record file fail,chn = %d!\n",chn);
	return -1;
    }

    if(type == REC_DTYPE_VIDEO)
	rsc_exception_handle(chn,CONT_STA_INIT,
		recsink->mpoint,recsink->fname);

    time_t time_bak = recsink->create_time;
    time_t time_now = time(NULL);
    int write_ing_flag = 0,wv_ret = 0, wa_ret = 0;
    int tlen = 0;

    if(type == REC_DTYPE_AUDIO)
	tlen = ctrl->audio_time;
    else
	tlen = ctrl->file_time;

    //SLOG(WRN,"+++++++++tlen = %d,chn = %d,on = %d\n",tlen,chn,ctrl->on);
    while(run_rec && ctrl->on)
    {
	//SLOG(PRI,"-------------proc record,chn = %d\n",chn);
	if(set_rec_param[chn])
	{
	    SLOG(PRI,"record param has changed,restart record!\n");
	    break;
	}
	if(type==REC_DTYPE_VIDEO && *valid != pre_valid)
	{// channel valid has changed
	    SLOG(PRI,"chn[%d] valid change:[%d] to [%d]\n",chn,
		    pre_valid,*valid);
	    break;
	}
	//record time arrived 
	time_now = time(NULL);
	if ((time_now+60 < time_bak) /* don't truncate file if diff in 60s */ 
		|| ((time_now - time_bak) >= tlen) 
		|| (recsink->file_size >= FILE_MAX_SIZE))
	{
	    if(rsc_default_cfg->control.chn_temp && *valid == 1)
	    {
		*valid = 0;/// restore normal record
		SLOG(PRI,"change valid from 1 to 0\n");
	    }
	    break;
	}

	//printf("---2---\n");
	//record video
	if((wv_ret=fill_record_sink(recsink,REC_DTYPE_VIDEO)<0))
	{
	    SLOG(ERR, "write video content err,ret = %d!\n",wv_ret);
	    ret = -1;
	    break;
	}
	//printf("---21---\n");
	if(type==REC_DTYPE_VIDEO && !write_ing_flag)
	{
	    rsc_exception_handle(chn,CONT_STA_WING,
		    recsink->mpoint,recsink->fname);
	    write_ing_flag = 1;
	}
	//printf("---3---\n");
	//record audio
	if((wa_ret=fill_record_sink(recsink,REC_DTYPE_AUDIO))<0)
	{
	    SLOG(ERR, "write audio content err,ret = %d!\n",wa_ret);
	    ret = -1;
	    break;
	}

	if(wv_ret == 1 && wa_ret == 1)
	    usleep(10000); /// audio frate is 50fps
	else if(wv_ret == 1 || wa_ret == 1)
	    usleep(10000);
    }

    deinit_record_sink(recsink);

    if(type == REC_DTYPE_VIDEO)
	rsc_exception_handle(chn,CONT_STA_WEND,recsink->mpoint,recsink->fname);

    return ret;
}

/*record thread control*/
static void *record_proc_thread(void *arg)
{
    int chn =(int)arg;
    struct rsc_record_param *param = &crnt_param;
    struct rsc_channel_param *cparam = &chn_param[chn];
    /**set audio rec switch is on***/
    pthread_mutex_lock(&rec_lock);
    crnt_param.control.audio_on = 1;
    pthread_mutex_unlock(&rec_lock);

    SLOG(PRI,"record chn thread:%d\n", cparam->chn);
    //printids(__FUNCTION__);
    while (run_rec)
    {
	/// sync record media param from global param
	if(set_rec_param[chn])
	{
	    set_rec_param[chn]=0;
	    memcpy(&cparam->media,&param->media,sizeof(param->media));
	}
	if(cparam->valid)
	{/// first:process channel record if it's valid
	    if(cparam->control.on && is_record())
	    {
		if(proc_record(chn,&cparam->media,
			    &cparam->control, &cparam->valid,REC_DTYPE_VIDEO)==0)
		    continue;
	    }
	}
	else 
	{/// second:process global record
	    if(param->control.on && is_record())
	    {
		if(proc_record(chn,&param->media,
			    &param->control,&cparam->valid,REC_DTYPE_VIDEO)==0)
		    continue;
	    }
	}

	sleep(1);
    }
    return NULL;
}

int is_record(void)
{
    int on = 0;
    int i = 0,chn_on = 0;

    pthread_mutex_lock(&rec_stat_mutex);
    for(i=0;i<RSC_REC_MAX_CHN;i++)
    {
	if(chn_param[i].valid && chn_param[i].control.on)
	{
	    chn_on = 1;
	    break;
	}
    }
    on = rec_status_val&&(crnt_param.control.on||chn_on);
    pthread_mutex_unlock(&rec_stat_mutex);
    //SLOG(PRI,">>>>>>rec_status_val = %d,chn_on = %d\n",rec_status_val,chn_on);
    return on;
}
int rsc_get_record(void)
{
    int on = 0;

    pthread_mutex_lock(&rec_stat_mutex);
    on = rec_status_val; 
    pthread_mutex_unlock(&rec_stat_mutex);
    return on;
}
int rsc_set_record(int flag)
{
    int on = 0;

    pthread_mutex_lock(&rec_stat_mutex);
    rec_status_val = flag; 
    pthread_mutex_unlock(&rec_stat_mutex);
    return on;
}

int rsc_start_record(int chn)
{
    if(chn < 0 || chn > RSC_REC_MAX_CHN)
    {
	SLOG(ERR, "invalid chn id:%d.\n",chn); 
	return -1;
    }

    if(chn < RSC_REC_MAX_CHN)
    {
	pthread_mutex_lock(&rec_lock);
	chn_param[chn].valid = 1;
	chn_param[chn].control.on = 1;
	pthread_mutex_unlock(&rec_lock);
    }
    else /// start all channels record
    {
	int i = 0;
	pthread_mutex_lock(&rec_lock);
	for(i=0;i<chn;i++)
	{
	    chn_param[i].valid = 0;
	    chn_param[i].control.on = 1;
	}
	crnt_param.control.on = 1;
	pthread_mutex_unlock(&rec_lock);
    }
    char mpoint[64]={0};
    if(rsc_get_avail_dev(0, mpoint)<0)
    {
	pthread_mutex_lock(&rec_stat_mutex);
	rec_status_val = 1; /// start put venc data to cbz 
	pthread_mutex_unlock(&rec_stat_mutex);
    }
    return 0;
}
int rsc_stop_record(int chn)
{
    if(chn < 0 || chn > RSC_REC_MAX_CHN)
    {
	SLOG(ERR, "invalid chn id:%d.\n",chn); 
	return -1;
    }
    if(chn < RSC_REC_MAX_CHN)
    {
	pthread_mutex_lock(&rec_lock);
	chn_param[chn].valid = 1;
	chn_param[chn].control.on = 0;
	pthread_mutex_unlock(&rec_lock);
    }
    else /// stop all channels record,and use global param
    {
	int i = 0;
	pthread_mutex_lock(&rec_lock);
	for(i=0;i<chn;i++)
	{
	    chn_param[i].valid = 1;
	    chn_param[i].control.on = 0;
	}
	crnt_param.control.on = 0;
	pthread_mutex_unlock(&rec_lock);
    }
    return 0;
}
int rsc_get_rec_params(struct rsc_record_param *param)
{
    if(!param)
    {
	SLOG(ERR, "parame invalid.\n"); 
	return -1;
    }
    /// read record media and control params from config file
    if(read_record_param(param) < 0)
    {
	SLOG(ERR, "read_record_param err\n");
	// if read failed,use default record parameters
	memcpy(param,rsc_default_cfg,sizeof(struct rsc_record_param));
	save_record_param(param);
	return 1;
    }
    else
    {
	/// set the record source params
	pthread_mutex_lock(&rec_lock);
	memcpy(&param->source, &rsc_default_cfg->source, sizeof(st_rsc_rec_source));
	memcpy(&param->sink, &rsc_default_cfg->sink, sizeof(st_rsc_rec_sink));
	pthread_mutex_unlock(&rec_lock);
    }
    return 0;
}

int rsc_set_rec_params(struct rsc_record_param *param)
{
    if(!param)
    {
	SLOG(ERR, "parame invalid \n"); 
	return -1;
    }

    /// update record media and control params
    pthread_mutex_lock(&rec_lock);
    memcpy(&crnt_param, param,sizeof(struct rsc_record_param));
    pthread_mutex_unlock(&rec_lock);

    int i = 0;
    for(i=0;i<RSC_REC_MAX_CHN;i++)
	set_rec_param[i] = 1;
    SLOG(PRI,"set record params.\n");

    if(save_record_param(&crnt_param) < 0)
    {
	SLOG(ERR, "save_record_param err\n");
	return -1;
    }
    return 0;
}

int rsc_get_chn_params(struct rsc_channel_param *param)
{
    if(!param)
    {
	SLOG(ERR, "parame invalid \n"); 
	return -1;
    }

    /// update channel record media and control params
    pthread_mutex_lock(&rec_lock);
    memcpy(param, &chn_param[param->chn], sizeof(struct rsc_channel_param));
    pthread_mutex_unlock(&rec_lock);

    return 0;
}

int rsc_set_chn_params(struct rsc_channel_param *param)
{
    if(!param)
    {
	SLOG(ERR, "parame invalid \n"); 
	return -1;
    }

    /// update channel record media and control params
    pthread_mutex_lock(&rec_lock);
    memcpy(&chn_param[param->chn], param,sizeof(struct rsc_channel_param));
    pthread_mutex_unlock(&rec_lock);

    return 0;
}

static void * record_audio_thread(void *arg)
{
    int chn = (int)arg;
    int ret = 0;
    struct rsc_channel_param *cparam = &chn_param[chn];

    //printids(__FUNCTION__);
    while(run_rec)
    {
	if(cparam->control.on && cparam->control.audio_on 
		&& cparam->control.audio_solo 
		&& cparam->control.audio_time>0 && is_record())
	{
	    if(proc_record(chn,&cparam->media,
			&cparam->control, &cparam->valid,REC_DTYPE_AUDIO)==0)
		continue;
	}
	sleep(1);
    }
    return (void*)ret;
}

int rsc_init_record(struct rsc_record_param *def_cfg)
{
    int i = 0;
    struct record_data_source *source = NULL;
    struct record_data_sink *sink = NULL;

    if(def_cfg == NULL)
    {
	SLOG(ERR,"rsc_record default config is null!\n");
	return -1;
    }

    source = &def_cfg->source;
    sink = &def_cfg->sink;

    /** check source is valid **/
    if(source == NULL)
    {
	SLOG(ERR,"invalid record source(null)!\n");
	return -1;
    }
    if(source->chn <= 0 || source->chn > RSC_REC_MAX_CHN)
    {
	SLOG(ERR,"invalid record source channel number[%d]!\n",source->chn);
	return -1;
    }

    /** check sink is valid **/
    if(sink == NULL)
    {
	SLOG(ERR,"invalid record sink(null)!\n");
	return -1;
    }
    if(sink->init_sink==NULL || sink->fill_sink==NULL || sink->exit_sink==NULL)
    {
	SLOG(ERR,"sink is not correctly configured!\n");
	return -1;
    }

    /* use assigned record default parameters */
    rsc_default_cfg = def_cfg;

    /// read record media and control params from config file
    if(read_record_param(&crnt_param) < 0)
    {
	SLOG(ERR, "read_record_param err\n");
	// if read fail,use default record parameters
	memcpy(&crnt_param,rsc_default_cfg,sizeof(struct rsc_record_param));
	save_record_param(&crnt_param);
    }
    else
    {
	/// copy record source & sink from pass-in parameter
	memcpy(&crnt_param.source, source, sizeof(st_rsc_rec_source));
	memcpy(&crnt_param.sink, sink, sizeof(st_rsc_rec_sink));
    }

    /// init channel parameters from global record parameters
    for(i=0;i<source->chn;i++)
    {
	chn_param[i].valid = 0;
	chn_param[i].chn = i;
	memcpy(&chn_param[i].media, &crnt_param.media, sizeof(st_rsc_rec_media));
	memcpy(&chn_param[i].control, &crnt_param.control, sizeof(st_rsc_rec_ctrl));
    }

    return 0;
}

int rsc_handle_record(void)
{
    int i = 0;
    int chns = rsc_default_cfg->source.chn;

    rsc_set_record(1);// we have sd card
    /** create channel record handle thread to parellel record **/
    for(i = 0; i < chns*2; i++)
    {
	if(i<chns && pthread_create(&record_tid[i], NULL, record_proc_thread, (void *)i) < 0)
	{
	    SLOG(ERR,"pthread_record create video record err!\n");
	    break;
	}
	if(i>=chns && pthread_create(&record_tid[i], NULL, record_audio_thread, (void *)(i-chns)) < 0)
	{
	    SLOG(ERR,"pthread_record create audio record err!\n");
	    break;
	}
    }
    // return the channels number that has pthread_create success.
    return i;
}

void rsc_exit_record(int chns_ok)
{
    int i = 0;	
    run_rec = 0;
    for(i = 0; i < chns_ok; i++)
	pthread_join(record_tid[i], NULL);

    SLOG(PRI,"record process exiting...\n");

    return ;
}


/**************** Audio Solo Record Handle Functions *****************/
/**
 * start audio solo record
 * @param[in] chn the assigned channel,= RSC_REC_MAX_CHN indicates all chns
 * @param[in] time record time length,unit is second
 * @param[in] user_data extral user data to write into audio record file
 */
int rsc_start_audio(int chn, int time/*unit:s*/, void *user_data,int len)
{
    if(chn < 0 || chn > RSC_REC_MAX_CHN)
    {
	SLOG(ERR,"chn[%d] is invalid!\n",chn);
	return -1;
    }
    if(time < 0)
    {
	SLOG(ERR,"time[%d] is invalid!\n",time);
	return -1;
    }
    if(!crnt_param.control.audio_solo)/// just ignore it
	return 0;

    if(len >= MAX_UDATA_SIZE)
	len = MAX_UDATA_SIZE-1;
    if(chn < RSC_REC_MAX_CHN)
    { ///single specific channel
	pthread_mutex_lock(&rec_lock);
	//chn_param[chn].valid = 1;
	chn_param[chn].control.audio_on = 1;
	chn_param[chn].control.audio_solo = 1;
	if(time > 0)/// keep last value when time  = 0
	    chn_param[chn].control.audio_time = time;
	pthread_mutex_unlock(&rec_lock);
	if(user_data!=NULL)
	{
	    memcpy(&rsc_user_data[chn][0],user_data,len);
	    rec_sinks[REC_DTYPE_AUDIO][chn].user_len = len;
	}
    }
    else
    {/// all channels
	int i = 0;
	pthread_mutex_lock(&rec_lock);
	for(i=0;i<chn;i++)
	{
	    //chn_param[i].valid = 0;
	    chn_param[chn].control.audio_on = 1;
	    chn_param[chn].control.audio_solo = 1;
	    if(time > 0)/// keep last value when time  = 0
		chn_param[chn].control.audio_time = time;
	}
	//crnt_param.control.audio_on = 1;
	//crnt_param.control.audio_solo = 1;
	//crnt_param.control.audio_time = time;
	pthread_mutex_unlock(&rec_lock);
	for(i=0;i<chn && user_data!=NULL;i++)
	{
	    memcpy(&rsc_user_data[i][0],user_data,len);
	    rec_sinks[REC_DTYPE_AUDIO][i].user_len = len;
	}
    }
    return 0;
}

int rsc_stop_audio(int chn, void *user_data,int len)
{
    if(chn < 0 || chn > RSC_REC_MAX_CHN)
    {
	SLOG(ERR,"chn[%d] is invalid!\n",chn);
	return -1;
    }
    if(!crnt_param.control.audio_solo)/// just ignore it
	return 0;

    if(chn < RSC_REC_MAX_CHN)
    { ///single specific channel
	pthread_mutex_lock(&rec_lock);
	//chn_param[chn].valid = 1;
	chn_param[chn].control.audio_on = 0;
	pthread_mutex_unlock(&rec_lock);
	if(user_data!=NULL)
	{
	    memcpy(&rsc_user_data[chn][0],user_data,len);
	    rec_sinks[REC_DTYPE_AUDIO][chn].user_len = len;
	}
    }
    else
    {/// all channels
	int i = 0;
	pthread_mutex_lock(&rec_lock);
	for(i=0;i<chn;i++)
	{
	    //chn_param[i].valid = 0;
	    chn_param[chn].control.audio_on = 0;
	    chn_param[chn].control.audio_solo = 0;
	}
	//crnt_param.control.audio_on = 0;
	pthread_mutex_unlock(&rec_lock);
	for(i=0;i<chn && user_data!=NULL;i++)
	{
	    memcpy(&rsc_user_data[i][0],user_data,len);
	    rec_sinks[REC_DTYPE_AUDIO][i].user_len = len;
	}
    }
    return 0;
}

int is_audio_record(void)
{
    return rec_status_val&&crnt_param.control.audio_on;
}

int is_audio_solo(void)
{
    int i = 0;
    int solo_on = 0;
    for(i=0;i<RSC_REC_MAX_CHN;i++)
    {
	if(chn_param[i].control.audio_on && chn_param[i].control.audio_solo
		&& chn_param[i].control.audio_time>0)
	{
	    solo_on = 1;
	    break;
	}
    }
    return solo_on;
}

int rsc_get_recording_filename(char fname[][128],int len)
{
    if(fname == NULL)
    {
	SLOG(ERR,"fname is null!\n");
	return -1;
    }
    if(len < 2*RSC_REC_MAX_CHN)
    {
	SLOG(ERR,"len is less than %d!\n",2*RSC_REC_MAX_CHN);
	return -1;
    }
    int i = 0;
    for(;i<RSC_REC_MAX_CHN;i++)
    {
	strncpy(fname[i],rec_sinks[REC_DTYPE_VIDEO][i].fname,128);
	strncpy(fname[i+RSC_REC_MAX_CHN],rec_sinks[REC_DTYPE_AUDIO][i].fname,128);
    }
    return 0;
}


